iT邦幫忙

2025 iThome 鐵人賽

DAY 18
0
Rust

Rust 後端入門系列 第 18

Day18 Axum 資料庫 PgPool 與連線管理

  • 分享至 

  • xImage
  •  

為什麼要用連線池(PgPool)

  • 每次建立 DB 連線都會造成負擔(網路 handshake、驗證、session 建立),直接為每一個 HTTP 請求建立連線會導致明顯效能問題與連線耗盡。
  • PgPool 提供可重複使用的連線、限制同時連線數量與管理單位時間內的連線壽命,能提升效能並保護資料庫不被過載。
  • 在 async web(如 Axum)中,PgPool 是非同步安全的共享資源(Clone cheap handle),適合注入到 handler。

建立 PgPool 的方式(connect / connect_lazy / builder)

  • 基本 connect(立即建立並嘗試連線):

    use sqlx::PgPool;
    
    let pool = PgPool::connect("postgres://user:pw@localhost/db").await;
    
  • connect_lazy(建立 pool handle,但不立即建立實際連線,直到第一次使用):

    let pool = PgPool::connect_lazy("postgres://user:pw@localhost/db");
    

    connect_lazy 可讓應用啟動更快(不等待 DB 可用),但第一次請求會承擔建立連線的延遲。

  • 使用 PgPoolOptions::new()(使用 builder pattern 的好處:可設定多種參數):

    use sqlx::postgres::PgPoolOptions;
    use std::time::Duration;
    
    let pool = PgPoolOptions::new()
        .max_connections(10)
        .connect_timeout(Duration::from_secs(5))
        .connect("postgres://user:pw@localhost/db").await;
    

常用配置參數說明

下面列出常用的配置與實務建議數值(需依業務/DB 能力調整):

  • max_connections(u32)
    • 說明:Pool 中同時可打開的最大 DB 連線數(例如 5、10、50)。
    • 建議:根據資料庫資源(Postgres 的 max_connections)與應用併發量設定。每個應用實例的 max_connections 乘上實例數應小於 DB 最大允許連線數。
  • connect_timeout(Duration)
    • 說明:建立連線時的超時,避免啟動或第一次連線無限等待。
    • 建議:設定 2–10 秒,視網路延遲而定。
  • acquire_timeout(Duration)
    • 說明:當 pool 已達 max_connections,嘗試取得可用連線的等待上限。
    • 建議:避免無限等待,設定合理值(例如 2–10 秒),若超時可回 503 或其它錯誤處理。
  • max_lifetime(Duration)
    • 說明:連線在 pool 中存活的最長時間,超過會被關閉並以新連線取代(避免長時間的 stale connections)。
    • 建議:設定為數分鐘到數小時(例如 30 分鐘 ~ 1 小時),但要考量 DB 與 network 的特性。
  • idle_timeout(Duration)
    • 說明:連線在 idle 狀態超過該時間會被關閉(節省 DB 資源)。
    • 建議:視流量與成本調整。
  • statement cache(prepared statement cache)
    • sqlx 在執行參數化查詢時會做一些預處理,若使用 sqlx::query_as! 或 query! ,sqlx 可在 runtime cache prepared statement,有助於效能。sqlx 本身會有一些快取行為,注意不要濫用動態 SQL 導致無法有效 cache。

在 Axum 中注入與使用 PgPool

  • 把 PgPool 放到 axum::Extension(shared state):

    use axum::{Router, Extension};
    use sqlx::PgPool;
    
    let pool: PgPool = /* 建好 */;
    let app = Router::new()
    					.layer(Extension(pool));
    
  • 在 handler 裡使用 Extension extractor:

    use axum::extract::Extension;
    use sqlx::PgPool;
    
    async fn handler(Extension(pool): Extension<PgPool>) -> String {
        // 使用 pool
        let rec = sqlx::query!("SELECT 1 as v")
            .fetch_one(&pool)
            .await
            .unwrap();
        format!("v = {}", rec.v)
    }
    

設計要點:

  • PgPool 是 Clone cheap 的型別:在多個 handler/route/shared state 中 clone 是常見做法。
  • 不要把單一連線(Connection)放到 Extension,應該共享 Pool。

上一篇
Day 17 Axum CRUD(PostgreSQL版本)
下一篇
Day 19 Axum 加入快取(Redis)與資料一致性策略
系列文
Rust 後端入門25
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言